import {
  CaretDownFilled,
  LoadingOutlined,
  MinusSquareOutlined,
  PlusSquareOutlined,
} from '@ant-design/icons';
import classNames from 'classnames';
import React, {
  cloneElement,
  isValidElement,
  useContext,
  useMemo,
} from 'react';
import TreeContext from './Context/TreeContext';
import Indent from './Indent';
import type { TreeNodeProps } from './interface';

const ICON_OPEN = 'open';
const ICON_CLOSE = 'close';

function TreeNode(props: TreeNodeProps) {
  const {
    eventKey,
    title,
    isStart,
    isEnd,
    data,
    expanded,
    loaded,
    loading,
    level,
    checked,
    halfChecked,
    selected,
  } = props;

  const {
    showCheckableBox = true,
    disabled = false,
    disableCheckbox = false,
    icon: nodeIcon,
    isLeaf,
    selectable = true,
  } = data;
  const {
    keyEntities,
    prefixCls,
    treeNodeClassName,
    treeNodeStyle,
    nodeContentWrapperClassName,
    nodeContentWrapperStyle,
    treeSelectable,
    treeShowCheckableBox, //默认false
    disabled: treeDisabled, //默认false
    icon: globalIcon,
    switcherIcon,
    indentWidth,
    draggable,
    titleRender,
    onNodeExpand,
    onNodeCheck,
    onNodeSelect,
    onDragStart,
    onDragEnter,
    onDragOver,
    onDragLeave,
    onDragEnd,
    onDrop,
    hoverKey,
    loadData,
    showLine,
  } = useContext(TreeContext)!;

  const isEndNode = isEnd[isEnd.length - 1];

  //-------------dragAndDrop-------------------
  const mergedDraggable = useMemo(() => {
    let mergedDraggable;
    if (!draggable) {
      mergedDraggable = false;
    } else if (draggable.nodeDraggable) {
      mergedDraggable = draggable.nodeDraggable(data);
    } else {
      mergedDraggable = true;
    }
    return mergedDraggable;
  }, [draggable, data]);

  const hasChildren = () => {
    const entity = keyEntities[eventKey];
    // console.log('--1.hasChildren--entity--:', entity);
    return entity && entity.children && entity.children.length ? true : false;
  };

  const isLeafFn = () => {
    if (typeof loadData === 'function') {
      if (isLeaf === false) {
        return false;
      }
      return isLeaf || (loaded && !hasChildren());
    }
    return !hasChildren();
  };

  //注： treeDisabled默认false, 为 true时以treeDisabled为准
  const isCheckBoxDisabled = useMemo(() => {
    if (treeDisabled) {
      return true;
    } else if (disabled) {
      return true;
    } else {
      return !!disableCheckbox;
    }
  }, [treeDisabled, disabled, disableCheckbox]);

  //注treeShowCheckableBox 默认 false,以treeCheckable为准
  const isShowCheckBox = useMemo(() => {
    if (treeShowCheckableBox === false) {
      return false;
    } else {
      return !!showCheckableBox;
    }
  }, [treeShowCheckableBox, showCheckableBox]);

  // treeSelectable默认true
  const isSelectDisabled = useMemo(() => {
    if (treeDisabled) {
      return true;
    } else if (treeSelectable === false) {
      return true;
    } else {
      return !selectable;
    }
  }, [treeDisabled, treeSelectable, selectable]);

  // ==================== Render: Drag Handler ====================
  // 要么都有dragIcon，要么都没有，不能拖动时有disabled 样式
  const renderDragHandler = useMemo<React.ReactNode>(() => {
    return draggable && isValidElement(draggable?.icon) ? (
      <div
        className={classNames(`${prefixCls}-draggable-icon`, {
          [`${prefixCls}-draggable-icon-disabled`]: !mergedDraggable,
        })}
      >
        {draggable?.icon}
      </div>
    ) : null;
  }, [draggable, mergedDraggable]);

  // ====================== Render: Switcher ======================
  // 无论是否有siwtcherIcon都会有ym-tree-switcher占位框
  const renderSwitcher = () => {
    let switcherBlock: React.ReactElement;
    if (loading) {
      switcherBlock = (
        <div
          className={classNames(
            `${prefixCls}-switcher`,
            `${prefixCls}-switcher-noop`,
          )}
        >
          <LoadingOutlined className={`${prefixCls}-switcher-loading-icon`} />
        </div>
      );
    } else if (isLeafFn()) {
      switcherBlock = (
        <div
          className={classNames(
            `${prefixCls}-switcher`,
            `${prefixCls}-switcher-noop`,
          )}
        >
          {showLine && <div className={`${prefixCls}-switcher-leaf-line`} />}
        </div>
      );
    } else {
      const switcherIconCls = `${prefixCls}-switcher-icon`;
      let switcherIconDom: React.ReactElement;

      if (isValidElement(switcherIcon)) {
        switcherIconDom = cloneElement(switcherIcon, {
          // @ts-ignore
          className: classNames(
            switcherIconCls,
            switcherIcon.props.className || '',
          ),
        });
      } else if (showLine) {
        switcherIconDom = expanded ? (
          <MinusSquareOutlined className={switcherIconCls} />
        ) : (
          <PlusSquareOutlined className={switcherIconCls} />
        );
      } else {
        switcherIconDom = <CaretDownFilled className={switcherIconCls} />;
      }
      switcherBlock = (
        <div
          className={classNames(
            `${prefixCls}-switcher`,
            `${prefixCls}-switcher_${expanded ? ICON_OPEN : ICON_CLOSE}`,
          )}
          onClick={(e) => {
            if (loading) return;
            onNodeExpand(e, props);
          }}
        >
          {switcherIconDom}
        </div>
      );
    }

    return switcherBlock;
  };

  // ====================== Render: Checkbox ======================
  // treeShowCheckableBox === true,showCheckableBox === false 时，不渲染checkbox,title会对不齐
  const renderCheckbox = () => {
    if (!isShowCheckBox) {
      return null;
    }

    return (
      <div
        className={classNames(
          `${prefixCls}-checkbox`, //未选
          {
            [`${prefixCls}-checkbox-checked`]: !isCheckBoxDisabled && checked, //选中
            [`${prefixCls}-checkbox-indeterminate`]:
              !isCheckBoxDisabled && !checked && halfChecked, //半选中
            [`${prefixCls}-checkbox-disabled`]: isCheckBoxDisabled, //禁止时：只有选中和未选中状态
          },
        )}
        onClick={(e) => {
          if (isCheckBoxDisabled) return;
          onNodeCheck(e, props);
        }}
      >
        <div className={`${prefixCls}-checkbox-inner`} />
      </div>
    );
  };

  const handleSelectClick = (e: React.MouseEvent<HTMLElement>) => {
    if (isSelectDisabled) {
      if (isCheckBoxDisabled) return;
      onNodeCheck(e, props);
    } else {
      onNodeSelect(e, props);
    }
  };

  // ==================== Render: Title + Icon ====================
  const renderSelector = () => {
    const wrapClass = `${prefixCls}-node-content-wrapper`;

    // Icon
    const currentIcon = nodeIcon ?? globalIcon;
    const renderIcon = currentIcon ? (
      <div
        className={classNames(`${wrapClass}-icon`)}
        onClick={handleSelectClick}
      >
        {typeof currentIcon === 'function' ? currentIcon(data) : currentIcon}
      </div>
    ) : null;
    // Title
    let titleNode: React.ReactNode;
    if (typeof title === 'function') {
      titleNode = title(data);
    } else if (titleRender) {
      titleNode = titleRender(data);
    } else {
      titleNode = title;
    }
    const renderTitle = (
      <div className={`${wrapClass}-title`} onClick={handleSelectClick}>
        {titleNode}
      </div>
    );
    return (
      <div
        title={typeof title === 'string' ? title : ''}
        className={classNames(`${wrapClass}`, {
          [`${nodeContentWrapperClassName}`]: !!nodeContentWrapperClassName,
          [`${wrapClass}-noicon`]: !renderIcon ? true : false,
          [`${wrapClass}-selectable`]: !isSelectDisabled,
          [`${wrapClass}-selected`]: !isSelectDisabled && selected,
          [`${wrapClass}-disabled`]: !!treeDisabled,
        })}
        style={nodeContentWrapperStyle}
      >
        {renderIcon}
        {renderTitle}
      </div>
    );
  };

  //-------dragAndDrop----------
  const handleDragStart: React.DragEventHandler<HTMLDivElement> = (e) => {
    e.stopPropagation();
    onDragStart({ event: e, node: data });
  };

  const handleDragEnter: React.DragEventHandler<HTMLDivElement> = (e) => {
    e.stopPropagation();
    onDragEnter({ event: e, node: data });
  };

  const handleDragOver: React.DragEventHandler<HTMLDivElement> = (e) => {
    e.stopPropagation();
    onDragOver({ event: e, node: data });
  };

  const handleDragLeave: React.DragEventHandler<HTMLDivElement> = (e) => {
    e.stopPropagation();
    onDragLeave({ event: e, node: data });
  };

  const handleDrop: React.DragEventHandler<HTMLDivElement> = (e) => {
    e.preventDefault();
    onDrop({ event: e, node: data });
  };

  //全局捕获
  const handleDragEnd: React.DragEventHandler<HTMLDivElement> = (e) => {
    e.stopPropagation();
    onDragEnd({ event: e, node: data });
  };

  return (
    <div
      className={classNames(`${prefixCls}-treenode`, treeNodeClassName, {
        [`${prefixCls}-treenode-disabled`]: !!treeDisabled,
        [`${prefixCls}-treenode-switcher-${expanded ? 'open' : 'close'}`]:
          !isLeafFn(),
        [`${prefixCls}-treenode-checkbox-checked`]:
          !isCheckBoxDisabled && checked,
        [`${prefixCls}-treenode-checkbox-indeterminate`]:
          !isCheckBoxDisabled && !checked && halfChecked,
        [`${prefixCls}-treenode-selected`]: !isSelectDisabled && selected,
        [`${prefixCls}-treenode-drag-active`]: hoverKey === eventKey,
        [`${prefixCls}-treenode-leaf-last`]: isEndNode,
      })}
      style={{
        ...treeNodeStyle,
        // @ts-ignore
        '--left':
          indentWidth * (level! + 1 + (showCheckableBox ? 1 : 0)) + 'px',
      }}
      draggable={draggable === undefined ? false : true}
      onDragStart={draggable !== undefined ? handleDragStart : undefined}
      onDragEnter={draggable !== undefined ? handleDragEnter : undefined}
      onDragOver={draggable !== undefined ? handleDragOver : undefined}
      onDragLeave={draggable !== undefined ? handleDragLeave : undefined}
      onDrop={draggable !== undefined ? handleDrop : undefined}
      onDragEnd={draggable !== undefined ? handleDragEnd : undefined}
    >
      <Indent
        prefixCls={prefixCls}
        level={level!}
        isStart={isStart}
        isEnd={isEnd}
        width={indentWidth}
      />
      {renderDragHandler}
      {renderSwitcher()}
      {renderCheckbox()}
      {renderSelector()}
    </div>
  );
}

export default TreeNode;
